using System;
using System.IO;
using System.IO.IsolatedStorage;

public sealed class Log
{
    private enum LogLevel
    {
        Information,
        Warning,
        Error
    }

    private sealed class StreamReaderProxy
    {
        public StreamReaderProxy(Stream stream)
        {
        }
    }

    private const string LogFilename = "Log.txt";
    private const string SessionLogFilename = "SessionLog.txt";

    private readonly IsolatedStorageFile _store;
    private readonly object _logStreamLock = new object();
    private readonly Stream _logStream;
    private readonly StreamWriter _logWriter;

    private Log(string logFilename, bool clear)
    {
        _store = IsolatedStorageFile.GetUserStoreForApplication();
        _logStream = _store.FileExists(logFilename)
                         ? _store.OpenFile(logFilename, FileMode.Open)
                         : _store.CreateFile(logFilename);
        _logStream.Seek(0, clear ? SeekOrigin.Begin : SeekOrigin.End);
        _logWriter = new StreamWriter(_logStream);
    }

    private const string LogMessageFormat = "{0}: {1} : {2}\n";

    public void AddInformation(string message, params object[] args)
    {
        var formattedMessage = string.Format(message, args);
        lock (_logStreamLock)
        {
            _logWriter.Write(string.Format(LogMessageFormat, LogLevel.Information, DateTime.Now, formattedMessage));
            _logWriter.Flush();
        }
    }

    public void AddWarning(string message, params object[] args)
    {
        var formattedMessage = string.Format(message, args);
        lock (_logStreamLock)
        {
            _logWriter.Write(string.Format(LogMessageFormat, LogLevel.Warning, DateTime.Now, formattedMessage));
            _logWriter.Flush();
        }
    }

    public void AddError(string message, params object[] args)
    {
        var formattedMessage = string.Format(message, args);
        lock (_logStreamLock)
        {
            _logWriter.Write(string.Format(LogMessageFormat, LogLevel.Error, DateTime.Now, formattedMessage));
            _logWriter.Flush();
        }
    }

    public void AddError(string message, Exception ex, params object[] args)
    {
        var formattedMessage = string.Format(message, args);
        lock (_logStreamLock)
        {
            _logWriter.Write(string.Format(LogMessageFormat, LogLevel.Error, DateTime.Now, formattedMessage));
            _logWriter.Write(string.Format(LogMessageFormat, LogLevel.Error, DateTime.Now, ex.Message));
            _logWriter.Flush();
        }
    }

    public string Contents
    {
        get
        {
            var reader = new StreamReader(_logStream);
            var currentPos = _logStream.Position;
            _logStream.Seek(0, SeekOrigin.Begin);
            var result = reader.ReadToEnd();
            _logStream.Seek(currentPos, SeekOrigin.Begin);

            return result;
            // Don't dispose the reader or the underlying stream will get closed
        }
    }

    private static Log _sessionLog;
    public static Log SessionLog
    {
        get { return _sessionLog ?? (_sessionLog = new Log(SessionLogFilename, true)); }
    }

    private static Log _persistentLog;
    public static Log PersistentLog
    {
        get
        {
            return _persistentLog ?? (_persistentLog = new Log(LogFilename, false));
        }
    }
}